/* Main Application */

#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_log.h"
#include "esp_console.h"
#include "esp_vfs_dev.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "driver/uart.h"
#include "fs.h"
#include "telnet_server.h"

/* Network initialization function declaration */
void network_init(void);

/* UART port for console */
#define CONSOLE_UART_NUM UART_NUM_0

static const char* TAG = "rteme-cli";

/* Helper functions */
static bool copy_file_content(const char* src_path, const char* dst_path);

/* Command handlers */
static int cmd_echo(int argc, char **argv);
static int cmd_version(int argc, char **argv);
static int cmd_ls(int argc, char **argv);
static int cmd_pwd(int argc, char **argv);
static int cmd_mkdir(int argc, char **argv);
static int cmd_rmdir(int argc, char **argv);
static int cmd_cat(int argc, char **argv);
static int cmd_rm(int argc, char **argv);
static int cmd_fsinfo(int argc, char **argv);
static int cmd_cp(int argc, char **argv);
static int cmd_mv(int argc, char **argv);
static int cmd_chmod(int argc, char **argv);
static int cmd_exit(int argc, char **argv);

/* Initialize NVS */
static void initialize_nvs(void)
{
    esp_err_t err = nvs_flash_init();
    if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        ESP_ERROR_CHECK( nvs_flash_erase() );
        err = nvs_flash_init();
    }
    ESP_ERROR_CHECK(err);
}

/* Initialize file system */
static void initialize_filesystem(void)
{
    fs_err_t ret = fs_init();
    if (ret != FS_OK) {
        ESP_LOGE(TAG, "Failed to initialize file system!");
    } else {
        ESP_LOGI(TAG, "File system initialized successfully!");
    }
}



/* Echo command with output redirection support */
static int cmd_echo(int argc, char **argv)
{
    if (argc < 2) {
        printf("Usage: echo <content> [> <file>]");
        return 1;
    }
    
    /* Check for output redirection */
    int i;
    int redir_pos = -1;
    for (i = 1; i < argc; i++) {
        if (strcmp(argv[i], ">") == 0) {
            redir_pos = i;
            break;
        }
    }
    
    if (redir_pos != -1) {
        /* Output redirection */
        if (redir_pos + 1 >= argc) {
            printf("Usage: echo <content> > <file>");
            return 1;
        }
        
        /* Join content before '>' */
        char content[512] = {0};
        for (i = 1; i < redir_pos; i++) {
            strcat(content, argv[i]);
            if (i < redir_pos - 1) {
                strcat(content, " ");
            }
        }
        
        const char* filename = argv[redir_pos + 1];
        
        /* Write content to file */
        void* file_ptr;
        fs_err_t ret = fs_open(filename, FS_MODE_WRITE | FS_MODE_CREATE | FS_MODE_TRUNCATE, &file_ptr);
        if (ret == FS_OK) {
            size_t written_bytes;
            ret = fs_write(file_ptr, content, strlen(content), &written_bytes);
            fs_close(file_ptr);
            
            if (ret == FS_OK) {
                printf("Content written to file successfully!\n");
                return 0;
            } else {
                printf("Failed to write to file!\n");
                return 1;
            }
        } else {
            printf("Failed to open file!\n");
            return 1;
        }
    } else {
        /* Regular echo to console */
        for (i = 1; i < argc; i++) {
            printf("%s", argv[i]);
            if (i < argc - 1) {
                printf(" ");
            }
        }
        printf("\n");
        return 0;
    }
}

/* Version command */
static int cmd_version(int argc, char **argv)
{
    printf("Version: 1.0.0\n");
    return 0;
}

/* ls command */
static int cmd_ls(int argc, char **argv)
{
    const char* path = ".";
    if (argc > 1) {
        path = argv[1];
    }
    
    void* dir_ptr;
    fs_err_t ret = fs_opendir(path, &dir_ptr);
    if (ret != FS_OK) {
        printf("Failed to open directory!\n");
        return 1;
    }
    
    char name[256];
    bool is_dir;
    bool is_done;
    
    do {
        ret = fs_readdir(dir_ptr, name, sizeof(name), &is_dir, &is_done);
        if (ret == FS_OK && !is_done) {
            if (is_dir) {
                printf("%s <DIR>\n", name);
            } else {
                size_t size;
                bool is_dir_stat;
                if (fs_stat(name, &size, &is_dir_stat) == FS_OK) {
                    printf("%s %10u\n", name, (unsigned int)size);
                } else {
                    printf("%s <ERROR>\n", name);
                }
            }
        }
    } while (ret == FS_OK && !is_done);
    
    fs_closedir(dir_ptr);
    return 0;
}

/* pwd command */
static int cmd_pwd(int argc, char **argv)
{
    printf("/fs\n");
    return 0;
}

/* mkdir command */
static int cmd_mkdir(int argc, char **argv)
{
    if (argc < 2) {
        printf("Usage: mkdir <dir>\n");
        return 1;
    }
    
    const char* path = argv[1];
    fs_err_t ret = fs_mkdir(path);
    if (ret != FS_OK) {
        printf("Failed to create directory!\n");
        return 1;
    } else {
        printf("Directory created successfully!\n");
        return 0;
    }
}

/* rmdir command */
static int cmd_rmdir(int argc, char **argv)
{
    if (argc < 2) {
        printf("Usage: rmdir <dir>\n");
        return 1;
    }
    
    const char* path = argv[1];
    fs_err_t ret = fs_rmdir(path);
    if (ret != FS_OK) {
        printf("Failed to remove directory!\n");
        return 1;
    } else {
        printf("Directory removed successfully!\n");
        return 0;
    }
}

/* cat command */
static int cmd_cat(int argc, char **argv)
{
    if (argc < 2) {
        printf("Usage: cat <file>\n");
        return 1;
    }
    
    const char* path = argv[1];
    void* file_ptr;
    fs_err_t ret = fs_open(path, FS_MODE_READ, &file_ptr);
    if (ret != FS_OK) {
        printf("Failed to open file!\n");
        return 1;
    }
    
    char buf[256];
    size_t bytes_read;
    
    do {
        ret = fs_read(file_ptr, buf, sizeof(buf) - 1, &bytes_read);
        if (ret == FS_OK && bytes_read > 0) {
            buf[bytes_read] = '\0';
            printf("%s", buf);
        }
    } while (ret == FS_OK && bytes_read > 0);
    
    fs_close(file_ptr);
    printf("\n");
    return 0;
}

/* rm command */
static int cmd_rm(int argc, char **argv)
{
    if (argc < 2) {
        printf("Usage: rm <file>\n");
        return 1;
    }
    
    const char* path = argv[1];
    fs_err_t ret = fs_remove(path);
    if (ret != FS_OK) {
        printf("Failed to delete file!\n");
        return 1;
    } else {
        printf("File deleted successfully!\n");
        return 0;
    }
}

/* fsinfo command */
static int cmd_fsinfo(int argc, char **argv)
{
    printf("File System: FATFS\nMount Point: /fs\nVersion: 1.0.0\n");
    return 0;
}

/* Helper function to copy file content from source to destination */
static bool copy_file_content(const char* src_path, const char* dst_path)
{
    /* Open source file for reading */
    void* src_file;
    fs_err_t ret = fs_open(src_path, FS_MODE_READ, &src_file);
    if (ret != FS_OK) {
        printf("Failed to open source file!\n");
        return false;
    }
    
    /* Open destination file for writing */
    void* dst_file;
    ret = fs_open(dst_path, FS_MODE_WRITE | FS_MODE_CREATE | FS_MODE_TRUNCATE, &dst_file);
    if (ret != FS_OK) {
        fs_close(src_file);
        printf("Failed to open destination file!\n");
        return false;
    }
    
    /* Copy content */
    char buf[256];
    size_t bytes_read, bytes_written;
    bool copy_success = true;
    
    do {
        ret = fs_read(src_file, buf, sizeof(buf), &bytes_read);
        if (ret != FS_OK && bytes_read == 0) {
            copy_success = false;
            break;
        }
        
        if (bytes_read > 0) {
            ret = fs_write(dst_file, buf, bytes_read, &bytes_written);
            if (ret != FS_OK || bytes_written != bytes_read) {
                copy_success = false;
                break;
            }
        }
    } while (bytes_read > 0);
    
    /* Close files */
    fs_close(src_file);
    fs_close(dst_file);
    
    return copy_success;
}

/* cp command - Copy file from source to destination */
static int cmd_cp(int argc, char **argv)
{
    if (argc < 3) {
        printf("Usage: cp <source> <destination>\n");
        return 1;
    }
    
    const char* src_path = argv[1];
    const char* dst_path = argv[2];
    
    /* Copy file content */
    if (copy_file_content(src_path, dst_path)) {
        printf("File copied successfully!\n");
        return 0;
    } else {
        printf("Failed to copy file!\n");
        return 1;
    }
}

/* mv command - Move or rename file from source to destination */
static int cmd_mv(int argc, char **argv)
{
    if (argc < 3) {
        printf("Usage: mv <source> <destination>\n");
        return 1;
    }
    
    const char* src_path = argv[1];
    const char* dst_path = argv[2];
    
    /* Try to rename file first (more efficient) */
    fs_err_t ret = fs_rename(src_path, dst_path);
    if (ret == FS_OK) {
        printf("File moved successfully!\n");
        return 0;
    } else if (ret == FS_NOT_FOUND) {
        printf("Source file not found!\n");
        return 1;
    } else {
        /* If rename fails, try copy and delete approach */
        if (copy_file_content(src_path, dst_path)) {
            /* Delete source file */
            ret = fs_remove(src_path);
            if (ret == FS_OK) {
                printf("File moved successfully!\n");
                return 0;
            } else {
                printf("File copied but failed to delete source!\n");
                return 1;
            }
        } else {
            printf("Failed to move file!\n");
            return 1;
        }
    }
}

/* chmod command - Change file permissions (FATFS doesn't support this) */
static int cmd_chmod(int argc, char **argv)
{
    if (argc < 3) {
        printf("Usage: chmod <mode> <file>\n");
        return 1;
    }
    
    /* Note: FATFS file system doesn't support file permissions
     * This command is provided for compatibility but doesn't perform any actual operation
     */
    printf("chmod: FATFS file system doesn't support file permissions\n");
    return 0;
}

/* exit command - Exit current console session */
static int cmd_exit(int argc, char **argv)
{
    printf("Exiting...\n");
    /* Return 0 to avoid device reboot */
    return 0;
}

/* Application entry point */
void app_main(void)
{
    /* Initialize NVS */
    initialize_nvs();
    
    /* Initialize file system */
    initialize_filesystem();
    
    /* Initialize network (WiFi) */
    network_init();
    
    /* Start Telnet server */
    xTaskCreate(telnet_server_init, "telnet_server", 4096, NULL, 5, NULL);
    
    /* Set standard input and output to non-blocking */
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);
    
    /* Start console REPL */
    esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT();
    repl_config.prompt = "rteme> ";
    /* Increase stack size for console_repl task to avoid stack overflow */
    repl_config.task_stack_size = 8192;
    
    esp_console_dev_uart_config_t uart_dev_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT();
    uart_dev_config.channel = CONSOLE_UART_NUM;
    
    esp_console_repl_t *repl = NULL;
    ESP_ERROR_CHECK(esp_console_new_repl_uart(&uart_dev_config, &repl_config, &repl));
    
    /* Note: help command is registered by esp_console_common_init, no need to register it manually */
    
    const esp_console_cmd_t cmd_echo_config = {
        .command = "echo",
        .help = "Echo input",
        .hint = "<content> [> <file>]",
        .func = &cmd_echo,
    };
    ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_echo_config));
    
    const esp_console_cmd_t cmd_version_config = {
        .command = "version",
        .help = "Show version",
        .hint = NULL,
        .func = &cmd_version,
    };
    ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_version_config));
    
    const esp_console_cmd_t cmd_ls_config = {
        .command = "ls",
        .help = "List directory contents",
        .hint = "[<dir>]",
        .func = &cmd_ls,
    };
    ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_ls_config));
    
    const esp_console_cmd_t cmd_pwd_config = {
        .command = "pwd",
        .help = "Show current directory",
        .hint = NULL,
        .func = &cmd_pwd,
    };
    ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_pwd_config));
    
    const esp_console_cmd_t cmd_mkdir_config = {
        .command = "mkdir",
        .help = "Create directory",
        .hint = "<dir>",
        .func = &cmd_mkdir,
    };
    ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_mkdir_config));
    
    const esp_console_cmd_t cmd_rmdir_config = {
        .command = "rmdir",
        .help = "Remove directory",
        .hint = "<dir>",
        .func = &cmd_rmdir,
    };
    ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_rmdir_config));
    
    const esp_console_cmd_t cmd_cat_config = {
        .command = "cat",
        .help = "Show file contents",
        .hint = "<file>",
        .func = &cmd_cat,
    };
    ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_cat_config));
    
    const esp_console_cmd_t cmd_rm_config = {
        .command = "rm",
        .help = "Delete file",
        .hint = "<file>",
        .func = &cmd_rm,
    };
    ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_rm_config));
    
    const esp_console_cmd_t cmd_fsinfo_config = {
        .command = "fsinfo",
        .help = "Show file system information",
        .hint = NULL,
        .func = &cmd_fsinfo,
    };
    ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_fsinfo_config));
    
    const esp_console_cmd_t cmd_cp_config = {
        .command = "cp",
        .help = "Copy file",
        .hint = "<source> <destination>",
        .func = &cmd_cp,
    };
    ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_cp_config));
    
    const esp_console_cmd_t cmd_mv_config = {
        .command = "mv",
        .help = "Move or rename file",
        .hint = "<source> <destination>",
        .func = &cmd_mv,
    };
    ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_mv_config));
    
    const esp_console_cmd_t cmd_chmod_config = {
        .command = "chmod",
        .help = "Change file permissions (FATFS doesn't support this)",
        .hint = "<mode> <file>",
        .func = &cmd_chmod,
    };
    ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_chmod_config));
    
    /* Register exit command */
    const esp_console_cmd_t cmd_exit_config = {
        .command = "exit",
        .help = "Exit current console session",
        .hint = NULL,
        .func = &cmd_exit,
    };
    ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_exit_config));
    
    ESP_ERROR_CHECK(esp_console_start_repl(repl));
}